// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_
#define STORAGE_LEVELDB_DB_LOG_READER_H_

#include <cstdint>

#include "db/log_format.h"
#include "leveldb/slice.h"
#include "leveldb/status.h"

namespace leveldb {

class SequentialFile;

namespace log {

class Reader {
public:
    // Interface for reporting errors.
    class Reporter {
    public:
        virtual ~Reporter();

        // Some corruption was detected. "bytes" is the approximate number
        // of bytes dropped due to the corruption.
        virtual void Corruption(size_t bytes, const Status& status) = 0;
    };

    // Create a reader that will return log records from "*file".
    // "*file" must remain live while this Reader is in use.
    //
    // If "reporter" is non-null, it is notified whenever some data is
    // dropped due to a detected corruption. "*reporter" must remain
    // live while this Reader is in use
    //
    // If "checksum" is true, verify checksums if avilable.
    //
    // The Reader will start reading at the first record located at physical
    // position >= intial_offset whthin the file.
    Reader(SequentialFile* file, Reporter* reporter, bool checksum,
           uint64_t intial_offset);

    Reader(const Reader&) = delete;
    Reader& operator=(const Reader&) = delete;

    ~Reader();

    // Read the next record into *record. Returns true if read
    // successfully, false if we hit end of the input. May use
    // "*scratch" as temporary storage. The contents filled in *record
    // will only be valid until the next mutating operation on this
    // reader of the next mutation to * scratch.
    bool ReadRecord(Slice* record, std::string* scratch);

    // Returns the physical offset of the last record returned by ReadRecord.
    //
    // Undefined before the first call to ReadRecord.
    uint64_t LastRecordOffset();

private:
    // Extend record bytes with the following special values
    enum {
        kEof = kMaxRecordType + 1,
        // Returned whenever we find an invalid physical record.
        // Currently there are three situations in which this happens:
        // * The record has an invalid CRC (ReadPhysicalRecord reports a drop)
        // * The record is a 0-length record (No drop is reported)
        // * The record is below constructor's intial_offset (No drop is reported)
        kBadRecord = kMaxRecordType + 2
    };

    // Skips all blocks that are completely before "intial_offset_".
    //
    // Returns true on success. Handles reporting.
    bool SkipToInitialBlock();

    // Return type, or one of the preceding special values
    unsigned int ReadPhysicalRecord(Slice* result);

    // Reports dropped bytes to the reporter.
    // buffer_ must be updated to remove the dropped bytes prior to invocation.
    void ReportCorruption(uint64_t bytes, const char* reason);
    void ReportDrop(uint64_t bytes, const Status& reason);

    SequentialFile* const file_;
    Reporter* const reporter_;
    bool const checksum_;
    char* const backing_store_;
    Slice buffer_;
    bool eof_;  // Last Read() indicated EOF by returning < kBlockSize

    // Offset of the last record returned by ReadRecord.
    uint64_t last_record_offset_;
    // Offset of the first location past the end of buffer_.
    uint64_t end_of_buffer_offset_;

    // Offset at which to start looking for the first record to return
    uint64_t const initial_offset_;

    // True if we are ersynchronizing after a seek (intial_offset_ > 0). In
    // particular, a run of kMiddleType and kLastType records can be silently
    // skipped in this mode
    bool resyncing_;
};

} // namespace log
} // namespace leveldb

#endif  // STORAGE_LEVELDB_DB_LOG_READER_H_
